package com.gwt.dispatcher.dispatcher.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.gwt.dispatcher.dispatcher.shared.Action;
import com.gwt.dispatcher.dispatcher.shared.CachableAction;
import com.gwt.dispatcher.dispatcher.shared.CachableOnContextAction;
import com.gwt.dispatcher.dispatcher.shared.Result;
import com.gwt.dispatcher.dispatcher.shared.services.ServerDispatcher;
import com.gwt.dispatcher.dispatcher.shared.services.ServerDispatcherAsync;


/**
 * <p>
 * This is the entry point for sending an action to the backend server.
 * These are the steps to accomplish this:
 * </p>
 * 
 * <ol>
 * 
 *  	<li>CLIENT SIDE.
 *  		<ul>
 *  
 *  		<li> Create the shared Data Structure to be returned from
 *  		   the server side (remember that this one should implement
 *  		   "Result")	
 *  
 *  		<li> Create your particular action (extending from Action)
 *  
 *  		</ul>
 *  
 *  	<li> SERVER SIDE.
 *  		<ul>
 *  
 *  		<li>Create the handler for your particular Action (extending
 *  		   from ActionHandler)
 *  
 *  		<li>Link the Action and ActionHandler in the ActionHandlerManager
 *  		   just to let SNM GUI know that every action of that type that is
 *  		   coming must be served by that handler
 *  
 *  		</ul>
 *  	
 *      <li> CLIENT SIDE.
 *      	<ul>
 *      
 *      	<li>Create an instance of your action
 *      	
 *      	<li>Send it through the execute method of this class and
 *      	   provide a Callback handler to be sure you'll receive the
 *      	   "Result" back (or an error in a case of an exception, see
 * 			   com.google.gwt.user.client.rpc.AsyncCallback<T>)
 * 
 * 			</ul>
 *</ol>
 * 
 * @author cristian.n.miranda
 *
 */
public class Dispatcher {
	
	private static final ServerDispatcherAsync service = GWT.create(ServerDispatcher.class);
	
	private static CacheContext globalCache = new CacheContext();

	public static <T extends Result> void execute(Action<T> action, final AsyncCallback<T> callback) {

	    if ( action instanceof CachableAction<?> ) {
	        execute( (CachableAction<T>) action, callback);
	        return;
	    }
	    
	    doExecution(action, callback);
		
	}
	
	/**
	 * Internal usage method
	 */
	static <T extends Result> void doExecution(Action<T> action, final AsyncCallback<T> callback) {

	    service.execute(action, new AsyncCallback<T>() {
          public void onFailure(Throwable caught) {
            callback.onFailure(caught);
          }
          public void onSuccess(T result) {
            callback.onSuccess(result);
          }
	    });             

	}
	
	private static <T extends Result> void execute(final CachableAction<T> action,  final AsyncCallback<T> callback ) {
	    globalCache.getValue(action.getCacheUniqueId(), action, callback);
	}
	
	public static <T extends Result> void execute(CacheContext cacheContext, final CachableOnContextAction<T> action,  final AsyncCallback<T> callback ) {
	    
	    // if no context is specified, then fall-back to non caching approach
	    if ( cacheContext == null )
	        execute(action, callback);
	    else
	        cacheContext.getValue(action.getCacheUniqueId(), action, callback);
	}

}
